package org.jeecg.modules.aspect;


import cn.hutool.core.util.ObjectUtil;
import com.google.gson.Gson;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.jeecg.modules.annotation.RequestLog;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;


@Component
@Aspect
public class RequestLogAspect {
	private final static Logger LOGGER = LoggerFactory.getLogger(RequestLogAspect.class);

	private static final String LINE_SEPARATOR = System.lineSeparator();

//	@Pointcut("@annotation(com.mingda.pinganbank.annotation.RequestLog)")
//	public void requestLogInterFace() {
//	}

	@Pointcut("execution( * org.jeecg.modules.paycenter.controller.ABCBankController.*(..)) || " +
			"execution(* org.jeecg.modules.paycenter.controller.HyBankPayController.*(..))")
//@Pointcut("execution( * org.jeecg.modules.paycenter.controller.*.*(..))")
public void requestLog() {
	}


	@Before("requestLog()")
	public void doBefore(JoinPoint joinPoint) throws Throwable {
		// 开始打印请求日志
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();

		// 获取注解的描述信息
		String methodDescription = getAspectLogDescription(joinPoint);

		// 打印请求相关参数
		LOGGER.info("========================================== Start ==========================================");
		// 打印请求 url
		LOGGER.info("URL            : {}", request.getRequestURL().toString());
		// 打印描述信息
		LOGGER.info("Description    : {}", methodDescription);
		// 打印 Http method
		LOGGER.info("HTTP Method    : {}", request.getMethod());
		// 打印调用 controller 的全路径以及执行方法
		LOGGER.info("Class Method   : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
		// 打印请求的 IP
		LOGGER.info("IP             : {}", request.getRemoteAddr());
		// 打印请求入参
		LOGGER.info("Request Args   : {}", new Gson().toJson(joinPoint.getArgs()));
	}

	/**
	 * 在切点之后织入
	 *
	 * @throws Throwable
	 */
//	@After("requestLog()")
//	public void doAfter() throws Throwable {
//		// 接口结束后换行，方便分割查看
//		log.info("=========================================== End ===========================================" + LINE_SEPARATOR);
//	}

	/**
	 * 环绕
	 *
	 * @param proceedingJoinPoint
	 * @return
	 * @throws Throwable
	 */
	@Around("requestLog()")
	public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		long startTime = System.currentTimeMillis();
		Object result = proceedingJoinPoint.proceed();
		// 打印出参
		LOGGER.info("Response Args  : {}", new Gson().toJson(result));
		// 执行耗时
		LOGGER.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);

		LOGGER.info("=========================================== End ===========================================" + LINE_SEPARATOR);

		return result;
	}


	/**
	 * 获取切面注解的描述
	 *
	 * @param joinPoint 切点
	 * @return 描述信息
	 * @throws Exception
	 */
	public String getAspectLogDescription(JoinPoint joinPoint)
			throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		StringBuilder description = new StringBuilder("");
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					if(ObjectUtil.isNotNull(method.getAnnotation(RequestLog.class))) {
						description.append(method.getAnnotation(RequestLog.class).description());
						break;
					}
				}
			}
		}
		return description.toString();
	}
}

